using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using PHSRAG.Utility;
using BO = PHSRAG.Insight21.BusinessObjects;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using GenerateKey = PHSRAG.Insight21.BusinessObjects.ModuleKey;

namespace PHSRAG.Insight21.Admin
{
	/// <summary>
	/// The object represents the notion of a group of PIs (Principal Investigastors).
	/// It also encapsulates methods to retrieve a lab, add and update.
	/// </summary>
	public class Lab : BO.SaveableBusinessObject
	{
		#region Constants
		private static string LabKey = GenerateKey.MakeKey("LabKey_B93A4AE9-D394-47fb-BF43-0641196F5E62",GenerateKey.Admin);
		private const string GetAdminLabSprocName = "GetAdminLab";
		private const string GetAdminLabPIsSprocName = "GetAdminLabPIs";
		private const string GetAdminLabAccessSprocName = "GetAdminLabUserAccessDetails";
		private const string AddAdminLabSprocName = "AddAdminLab";
		private const string UpdatePIsSprocName = "UpdateAdminLabPIs";
		private const string DeletePIsSprocName = "DeleteAdminLabPI";
		private const string UpdateAccessSprocName = "UpdateAdminLabAccess";
		private const string DeleteAdminLabAccessSprocName = "DeleteAdminLabMemberAccess";
		private const string GetAvailablePIsSprocName = "GetPIList";
		private const string LabBasicInfoTableName = "Lab";
		private const string LabPIsTableName = "AssignedPIs";
		private const string LabAccessTableName = "Access";
		private const string AvailablePIsTableName = "AvailablePIs";
		#endregion

		#region Private Members
		private SqlCommand addLabCommand;
		private SqlCommand updatePIsCommand;
		private SqlCommand deletePIsCommand;
		private SqlCommand updateAccessCommand;
		private SqlCommand deleteAccessCommand;
		#endregion

		#region Constructor
		/// <summary>
		/// Intitializes the datacontainer.
		/// </summary>
		/// <param name="cachePolicy">Policy used for maintaining state.</param>
		public Lab(ICachePolicy cachePolicy) : base(cachePolicy)
		{
			DataContainer = new DataSet();
		}
		#endregion
 
		#region Public Properties
		/// <summary>
		/// Get the key to be used in all caching operations for this instance of the object.
		/// </summary>
		public static string CacheKey	{ get { return LabKey; } }

		/// <summary>
		/// Gets the ID of the Lab from the data container.
		/// </summary>
		public int Id 
		{
			get
			{
				return (int)DataContainer.Tables[LabBasicInfoTableName].Rows[0]["Id"];
			}
		}
		/// <summary>
		/// Gets the name of the lab from the data container.
		/// </summary>
		public string Name 
		{
			get
			{
				return (string)DataContainer.Tables[LabBasicInfoTableName].Rows[0]["Name"];
			}
		}
		/// <summary>
		/// Gets lab's description from the data container.
		/// </summary>
		public string Description 
		{
			get
			{
				return (string)DataContainer.Tables[LabBasicInfoTableName].Rows[0]["Description"];
			}
		}
		/// <summary>
		/// Gets lab's description from the data container.
		/// </summary>
		public string Organization
		{
			get
			{
				return (string)DataContainer.Tables[LabBasicInfoTableName].Rows[0]["FullyQualifiedName"];
			}
		}
		/// <summary>
		/// Checks if the PI list is loaded to the data container.
		/// </summary>
		public bool IsAvailablePIDataTableLoaded 
		{
			get { return DataContainer.Tables[AvailablePIsTableName] != null; }
		}
		/// <summary>
		/// Gets the datatable with lab access details. 
		/// </summary>
		public DataTable Access 
		{
			get { return DataContainer.Tables[LabAccessTableName]; }
		}
		/// <summary>
		/// Gets the datatable with assigned PI list. 
		/// </summary>
		public DataTable AssignedPIs 
		{
			get { return DataContainer.Tables[LabPIsTableName]; }
		}
		/// <summary>
		/// Gets the datatable with available PI list. 
		/// </summary>
		public DataTable AvailablePIs 
		{
			get { return DataContainer.Tables[AvailablePIsTableName]; }
		}
		/// <summary>
		/// Gets the datatable with lab basic information. 
		/// </summary>
		public DataTable BasicInfo 
		{
			get { return DataContainer.Tables[LabBasicInfoTableName]; }
		}
		#endregion

		#region Base Class Overrides
		/// <summary>
		/// Caches this instance using a specified key.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(LabKey, this);
		}
		/// <summary>
		/// Removes this instance from session.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(LabKey);
		}
		/// <summary>
		/// Loads the data container with data from repository based on the LabCommandType
		/// passed in.
		/// </summary>
		/// <param name="connectionString">The connection string to the data source.</param>
		/// <param name="args">arguments to be passed to the stored procedure.</param>
		public override void Load(string connectionString, params object[] args)
		{
			if (args.Length != 1)
				throw new Exception("Lab Load method requires one argument");
			DataAccess.LoadDataSet(DataContainer, LabBasicInfoTableName, 
				connectionString, GetAdminLabSprocName, DataAccess.BuildSqlParameterArray(
				"@labID", SqlDbType.Int, args[0]));
			DataAccess.LoadDataSet(DataContainer, LabPIsTableName, 
				connectionString, GetAdminLabPIsSprocName, DataAccess.BuildSqlParameterArray(
				"@labID", SqlDbType.Int, args[0]));
			DataAccess.LoadDataSet(DataContainer, LabAccessTableName,
				connectionString, GetAdminLabAccessSprocName, DataAccess.BuildSqlParameterArray(
				"@labID", SqlDbType.Int, args[0]));
		}

		/// <summary>
		/// Persists changes to the lab to the database.
		/// </summary>
		/// <param name="connectionString">The connection string to the data source.</param>
		/// <param name="args">arguments to be passed to the stored procedure.</param>
		public override void Save(string connectionString, params object[] args)
		{
			SqlConnection connection = new SqlConnection(connectionString);
			try
			{
				connection.Open();
				switch ((LabCommandType)args[0]) 
				{
					case LabCommandType.Add: 
						CreateAddLabCommand();
						ExecuteAddLab(connection);
						break;

					case LabCommandType.EditPIs:
						CreateEditLabPIsCommands();
						ExecutePIsUpdate(connection);
						break;

					case LabCommandType.EditAccess:
						CreateEditLabAccessCommands();
						ExecuteAccessUpdate(connection);
						break;

					default:
						throw new Exception("Unsupported LabCommandType");
				}
			}
			catch (Exception ex) 
			{
				throw ex;
			}
			finally
			{
				if (connection.State != ConnectionState.Closed)
					connection.Close();
			}
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Create the SqlCommand object used for the insertion made on the associated DataTable.
		/// </summary>
		private void CreateAddLabCommand()
		{
			if (addLabCommand == null) 
			{
				addLabCommand = new SqlCommand();
				addLabCommand.CommandText = AddAdminLabSprocName;
				addLabCommand.CommandType = CommandType.StoredProcedure;
				addLabCommand.CommandTimeout = Convert.ToInt32(Utility.Settings.GetKeyValue("Database.Insight.CommandTimeout", "0"));
				addLabCommand.Parameters.Add("@domainUserID", SqlDbType.Int).SourceColumn = "DomainUserId";
				addLabCommand.Parameters.Add("@organizationID", SqlDbType.Int).SourceColumn = "OrganizationId";
				addLabCommand.Parameters.Add("@name", SqlDbType.VarChar).SourceColumn = "Name";
				addLabCommand.Parameters.Add("@description", SqlDbType.VarChar).SourceColumn = "Description";
			}

		}
		/// <summary>
		/// Create the SqlCommand object used for the updates made on the associated DataTable.
		/// </summary>
		private void CreateEditLabPIsCommands()
		{
			if (updatePIsCommand == null) 
			{
				updatePIsCommand = new SqlCommand();
				updatePIsCommand.CommandText = UpdatePIsSprocName;
				updatePIsCommand.CommandType = CommandType.StoredProcedure;
				updatePIsCommand.CommandTimeout = Convert.ToInt32(Utility.Settings.GetKeyValue("Database.Insight.CommandTimeout", "0"));
				updatePIsCommand.Parameters.Add("@labID", SqlDbType.Int).SourceColumn = "LabId";
				updatePIsCommand.Parameters.Add("@personID", SqlDbType.Int).SourceColumn = "PersonId";
			}

			if (deletePIsCommand == null)
			{
				deletePIsCommand = new SqlCommand();
				deletePIsCommand.CommandText = DeletePIsSprocName;
				deletePIsCommand.CommandType = CommandType.StoredProcedure;
				deletePIsCommand.CommandTimeout = Convert.ToInt32(Utility.Settings.GetKeyValue("Database.Insight.CommandTimeout", "0"));
				deletePIsCommand.Parameters.Add("@labID", SqlDbType.Int).SourceColumn = "LabId";
				deletePIsCommand.Parameters.Add("@personID", SqlDbType.Int).SourceColumn = "PersonId";
			}
		}

		/// <summary>
		/// Create the SqlCommand object used for the updates made on the associated DataTable.
		/// </summary>
		private void CreateEditLabAccessCommands()
		{
			if (updateAccessCommand == null) 
			{
				updateAccessCommand = new SqlCommand();
				updateAccessCommand.CommandText = UpdateAccessSprocName;
				updateAccessCommand.CommandType = CommandType.StoredProcedure;
				updateAccessCommand.CommandTimeout = Convert.ToInt32(Utility.Settings.GetKeyValue("Database.Insight.CommandTimeout", "0"));
				updateAccessCommand.Parameters.Add("@labID", SqlDbType.Int).SourceColumn = "LabId";
				updateAccessCommand.Parameters.Add("@personID", SqlDbType.Int).SourceColumn = "PersonId";
				updateAccessCommand.Parameters.Add("@rightBitMask", SqlDbType.Int).SourceColumn = "RightBitMask";
			}
			
			if (deleteAccessCommand == null) 
			{
				deleteAccessCommand = new SqlCommand();			
				deleteAccessCommand.CommandText = DeleteAdminLabAccessSprocName;
				deleteAccessCommand.CommandType = CommandType.StoredProcedure;
				deleteAccessCommand.CommandTimeout = Convert.ToInt32(Utility.Settings.GetKeyValue("Database.Insight.CommandTimeout", "0"));
				deleteAccessCommand.Parameters.Add("@labID", SqlDbType.Int).SourceColumn = "LabId";
				deleteAccessCommand.Parameters.Add("@personID", SqlDbType.Int).SourceColumn = "PersonId";
			}
		}
		/// <summary>
		/// Uses a data adapter to perform a lab insert into the database.
		/// </summary>
		/// <param name="connection">connection to be used to connect to the datastore..</param>
		private void ExecuteAddLab(SqlConnection connection) 
		{
			SqlDataAdapter adapter = new SqlDataAdapter();
			addLabCommand.Connection = connection;
			adapter.InsertCommand = addLabCommand;
			adapter.UpdateCommand = addLabCommand;
			adapter.DeleteCommand = addLabCommand; 
			adapter.Update(DataContainer, LabBasicInfoTableName);	
		}
		/// <summary>
		/// Uses a data adapter to perform a lab PIs updates.
		/// </summary>
		/// <param name="connection">connection to be used.</param>
		private void ExecutePIsUpdate(SqlConnection connection) 
		{
			SqlDataAdapter adapter = new SqlDataAdapter();
			updatePIsCommand.Connection = deletePIsCommand.Connection = connection;
			adapter.InsertCommand = updatePIsCommand;
			adapter.UpdateCommand = updatePIsCommand;
			adapter.DeleteCommand = deletePIsCommand;
			adapter.Update(DataContainer, LabPIsTableName);
		}
		/// <summary>
		/// Uses a data adapter to perform a lab Access updates.
		/// </summary>
		/// <param name="connection">connection to be used.</param>
		private void ExecuteAccessUpdate(SqlConnection connection) 
		{
			SqlDataAdapter adapter = new SqlDataAdapter();
			updateAccessCommand.Connection = deleteAccessCommand.Connection = connection;			
			adapter.InsertCommand = updateAccessCommand;
			adapter.UpdateCommand = updateAccessCommand;
			adapter.DeleteCommand = deleteAccessCommand;
			adapter.Update(DataContainer, LabAccessTableName);
		}
		/// <summary>
		/// Loads the PI list into the datacontainer.
		/// </summary>
		/// <param name="connection">The domain ID of the user whose PIs
		/// we are retrieving.</param>
		public void LoadAvailablePIs(int domainUserId)
		{
			DataAccess.LoadDataSet(DataContainer, AvailablePIsTableName, DataAccess.GetConnectionString(),
				GetAvailablePIsSprocName, DataAccess.BuildSqlParameterArray("@domainUserID", SqlDbType.Int, domainUserId));		
		}
		#endregion
	}

	/// <summary>
	/// Used in Load and Save method to determine the stored procedure to invoke.
	/// </summary>
	public enum LabCommandType
	{
		/// <summary>
		/// Adds a lab to the database.
		/// </summary>
		Add,
		/// <summary>
		/// Updates mappings between a lab and PIs.
		/// </summary>
		EditPIs,
		/// <summary>
		/// Updates mappings between a Lab and users.
		/// </summary>
		EditAccess
	}
}
